package dagsvc

import (
	"context"
	"fmt"
	corev1 "gluenet/internal/agent/api/core/v1"
	"gluenet/internal/agent/depend"
	"gluenet/internal/agent/service/promsvc"
	"gluenet/internal/agent/service/redissvc"
	"gluenet/internal/glue-srvp/adapter/metrics-cli/prom"
	"gluenet/utils/rpc/v2/define"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
)

func (svc *Service) DagServiceMap(dagServiceMap define.DagServiceMap) corev1.CommonResp {
	redisClient := redis.NewClient(&redis.Options{
		Addr:     depend.Produce[*redissvc.RedisConfig]().Addr,
		Password: depend.Produce[*redissvc.RedisConfig]().Password, // no password set
		DB:       0,                                                // use
		// default DB
	})
	_, err := redisClient.Ping(context.Background()).Result()
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	// Find the key nearest to the given end .
	// 1.Find all keys that match given guid.
	keys, err := redisClient.Keys(context.Background(), dagServiceMap.Guid[:strings.LastIndex(dagServiceMap.Guid, "#")]+"*").Result()
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	if len(keys) == 0 {
		svc.logger.Errorf("no key found")
		return corev1.NewCommonFailureResp("no key found")
	}
	// 2.Put all timestamps into a slice.
	timeStamp := make([]string, len(keys))
	for i, key := range keys {
		timeStamp[i] = key[strings.LastIndex(key, "#")+1:]
	}
	// 3.Store distance intervals into an array.
	step := make([]int, len(keys))
	for i, key := range timeStamp {
		m, _ := strconv.Atoi(dagServiceMap.Guid[strings.LastIndex(dagServiceMap.Guid, "#")+1:])
		n, _ := strconv.Atoi(key)
		step[i] = m - n
	}
	// 4.Find the minimum distance and get corresponding index.
	min := math.Abs(float64(step[0]))
	index := 0
	for i := 0; i < len(step); i++ {
		if math.Abs(float64(step[i])) < min {
			min = math.Abs(float64(step[i]))
			index = i
		}
	}
	result := redisClient.Get(context.Background(), dagServiceMap.Guid[:strings.LastIndex(dagServiceMap.Guid, "#")]+"#"+timeStamp[index]).Val()
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	return corev1.NewCommonSuccessRespWithData(result)
}

func (svc *Service) DagResponse(dagServiceMap define.DagResponse) corev1.CommonResp {
	err := prom.PromConnect(depend.Produce[*promsvc.PromConfig]().Api, depend.Produce[*promsvc.PromConfig]().ApiRange)
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	ciliumHttpResponseLatency, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":    "cilium_http_response_latence",
		"destination": dagServiceMap.DestinationPod,
		"source":      dagServiceMap.SourcePod,
		"port":        dagServiceMap.Port,
		"method":      dagServiceMap.Method,
		"url":         dagServiceMap.Url,
		"verdict":     dagServiceMap.Verdict,
		"protocol":    dagServiceMap.Protocol,
		"guid":        dagServiceMap.Guid,
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	ciliumHttpResponseLatenceCount, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":    "cilium_http_response_latence_count",
		"destination": dagServiceMap.DestinationPod,
		"source":      dagServiceMap.SourcePod,
		"port":        dagServiceMap.Port,
		"method":      dagServiceMap.Method,
		"url":         dagServiceMap.Url,
		"verdict":     dagServiceMap.Verdict,
		"protocol":    dagServiceMap.Protocol,
		"guid":        dagServiceMap.Guid,
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	ciliumHttpResponseLatenceErrCount, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":    "cilium_http_response_latence_count",
		"destination": dagServiceMap.DestinationPod,
		"source":      dagServiceMap.SourcePod,
		"port":        dagServiceMap.Port,
		"method":      dagServiceMap.Method,
		"url":         dagServiceMap.Url,
		"protocol":    dagServiceMap.Protocol,
		"guid":        dagServiceMap.Guid,
		"verdict":     "DROPPED",
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		svc.logger.Errorf(err.Error())
		return corev1.NewCommonFailureResp(err.Error())
	}
	latency := make(map[string]float64, len(ciliumHttpResponseLatency.Result))
	for _, v := range ciliumHttpResponseLatency.Result {
		latency[v.Metric["quantile"]] = v.Value[1].(float64)
	}
	var requestErrCount int
	var requestSuccessRate float64
	if len(ciliumHttpResponseLatenceErrCount.Result) == 0 {
		requestErrCount = 0
		requestSuccessRate = 1
	} else {
		requestErrCount = ciliumHttpResponseLatenceErrCount.Result[0].Value[1].(int)
		requestSuccessRate = 1 - (ciliumHttpResponseLatenceErrCount.Result[0].Value[1].(float64) / ciliumHttpResponseLatenceCount.Result[0].Value[1].(float64))
	}
	resp := define.DagResponseResp{
		TotalCount:  ciliumHttpResponseLatenceCount.Result[0].Value[1].(float64),
		Latency:     latency,
		ErrCount:    requestErrCount,
		SuccessRate: requestSuccessRate,
	}
	return corev1.NewCommonSuccessRespWithData(resp)
}
